package io.github.novaframe.quartz;

import io.github.novaframe.web.mapper.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created by Locki on 9/7/2017.
 */
@Slf4j
public class QuartzJobService {

    final SchedulerFactoryBean schedulerFactoryBean;
    final JsonMapper jsonMapper;

    public QuartzJobService(SchedulerFactoryBean schedulerFactoryBean, JsonMapper jsonMapper) {
        this.schedulerFactoryBean = schedulerFactoryBean;
        this.jsonMapper = jsonMapper;
    }

    /**
     * 获取单个任务
     */
    public QuartzJobModel getJob(String jobName, String jobGroup) throws SchedulerException {
        QuartzJobModel job = null;
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if (null != trigger) {
            job = new QuartzJobModel();
            job.setJobName(jobName);
            job.setJobGroup(jobGroup);
            job.setDescription("触发器: " + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.setJobStatus(triggerState.name());

            String cronExpression = trigger.getCronExpression();
            job.setCronExpression(cronExpression);
        }
        return job;
    }
    /**
     * 获取所有任务
     */
    public List<QuartzJobModel> getAllJobs() throws SchedulerException {
        List<QuartzJobModel> jobList = new ArrayList<>();

        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                QuartzJobModel job = new QuartzJobModel();
                job.setJobName(jobKey.getName());
                job.setJobGroup(jobKey.getGroup());
                job.setDescription("触发器: " + trigger.getKey());

                //触发器当前状态
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setJobStatus(triggerState.name());
                //
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCronExpression(cronExpression);
                }
                jobList.add(job);
            }
        }
        return jobList;
    }

    /**
     * 所有正在运行的job
     *
     * @return
     * @throws SchedulerException
     */
    public List<QuartzJobModel> getRunningJobs() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();

        List<QuartzJobModel> jobList = new ArrayList<>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            QuartzJobModel job = new QuartzJobModel();
            JobDetail jobDetail = executingJob.getJobDetail();
            JobKey jobKey = jobDetail.getKey();
            Trigger trigger = executingJob.getTrigger();
            job.setJobName(jobKey.getName());
            job.setJobGroup(jobKey.getGroup());
            job.setDescription("触发器:" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.setJobStatus(triggerState.name());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) trigger;
                String cronExpression = cronTrigger.getCronExpression();
                job.setCronExpression(cronExpression);
            }
            jobList.add(job);
        }
        return jobList;
    }


    /**
     * 添加任务
     */
    public boolean addJob(QuartzJobModel job) throws SchedulerException {
        if(!QuartzJobUtil.isValidExpression(job.getCronExpression())){
            log.error("error cron expression, jobName={}, jobGroup={}, cronExpression={}", job.getJobName(), job.getJobGroup(), job.getCronExpression());
            return false;
        }

        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        // 任务名称和任务组设置规则：    // 名称：task_1 ..    // 组 ：group_1 ..
        TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

        if (null == trigger) {
            // 不存在，创建一个
            //是否允许并发执行
            Class<? extends Job> clazz = QuartzJob.class;
            //if(job.getIsConcurrent()){
            //    clazz = ConcurrentQuartzJob.class;
            //} else {
            //    clazz = QuartzJob.class;
            //}

            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
            jobDetail.getJobDataMap().put(QuartzJob.JOB_DATA_KEY, jsonMapper.toJson(job));
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
            // 按新的表达式构建一个新的trigger
            trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            // trigger已存在，则更新相应的定时设置
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
        return true;
    }

    /**
     * 暂停任务
     */
    public boolean pauseJob(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.pauseJob(jobKey);
        return true;
    }

    /**
     * 恢复任务
     */
    public boolean resumeJob(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.resumeJob(jobKey);
        return true;
    }

    /**
     * 删除任务
     */
    public boolean deleteJob(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if(scheduler.checkExists(jobKey)){
            scheduler.deleteJob(jobKey);
        }
        return true;
    }

    /**
     * 立即执行一个任务
     */
    public void runJob(String jobName, String jobGroup) throws SchedulerException{
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新任务时间表达式
     * @param job
     * @throws SchedulerException
     */
    public void updateCronExpression(QuartzJobModel job) throws SchedulerException{
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
        //获取trigger，即在spring配置文件中定义的 bean id="myTrigger"
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        //表达式调度构建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
        //按新的cronExpression表达式重新构建trigger
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        //按新的trigger重新设置job执行
        scheduler.rescheduleJob(triggerKey, trigger);
    }
}
